<?php
/**
 * 所有的数据均是用户拥有权限下可查看操作的数据
 * Date: 2018/4/10
 * Time: 10:27
 * Author: zeronlee
 */

namespace App\Services\ChartAnalysis;

use App\Models\App;
use App\Models\Flaw\Vuls;
use App\Models\Ogz;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;

class Data
{

    /**
     * 机构
     */
    public $ogz = [];

    /**
     * 应用
     */
    public $app = [];

    /**
     * 分类
     */
    public $cate =[];

    /**
     * 等级
     */
    public $level =[];

    /**
     * @var array
     */
    private $appIds = [];

    /**
     * 统计类型,叶子节点或所有节点
     */
    private $onlyLeaf = false;

    /**
     * 时间筛选
     * @var bool
     */
    public $timeVaild = false;

    /**
     * 开始时间
     * @var string
     */
    public $startime;

    /**
     * 结束时间
     * @var string
     */
    public $endTime;

    /**
     * 不统计的状态
     */
    private $scopeStatus = [Vuls::PENDING,Vuls::IGNORE,Vuls::NOEFFECT];

    /**
     * 获取机构数据
     * @param $pid
     * @return mixed $ogz
     */
    public function getOgz ($pid) {

        return $this->setOgz($pid);
    }

    /**
     * 设置机构详细数据
     * @param $pid
     * @return mixed $ogz
     */
    public function setOgz ($pid) {
        $ogz = collect();
        //db object
        if (intval($pid))
            $ogz = Ogz::getChild($pid);
        //递归式明细
        $this->handleOgz($ogz);

        return $ogz;
    }

    /**
     * 处理机构层及明细数据
     * @param mixed $ogz
     * @return void
     */
    private function handleOgz ($ogz) {
        $accessIds = access()->hasOgzs();
        foreach ($ogz as $item) {
            //用户权限下的机构id
            if (in_array($item->id, $accessIds)) {

                $this->ogz[$item->pid][$item->id] = $item->name;

                if (isset($item->child)) {

                    $this->handleOgz($item->child);

                }
            }
        }
    }

    /**
     * 获取应用数据
     * @param array $ogzs
     * @return mixed $apps
     */
    public function getApp ($ogzs) {
        $apps = collect();
        if (is_array($ogzs)) {
            for ($i = 0; $i < count($ogzs); ++$i) {

                $value = App::getRowByColumn(['id', 'name'], $ogzs[$i]);
                if ($value) {
                    //机构拥有多个应用
                    foreach ($value as $item) {
                        //id数组
                        $this->appIds[] = $item->id;

                        //对应关系数组
                        $this->app[$ogzs[$i]][$item->id] = $item->name;
                        $apps->push($item);
                    }

                } else {
                    $this->app[$ogzs[$i]] = [];

                }
            }

        }
        return $apps;
    }

    /**
     * 获取分类数据TOP x
     * @param array $appIds
     * @return mixed $category
     */
    public function getCategory ($appIds) {
        $category = collect();
        if (is_array($appIds)) {

            $builder = DB::table('category as c')->select([
                'c.id',
                'c.name',
                'c.pid',
                DB::raw('count(v.id) as num')
            ])->leftJoin('vuls as v', 'v.category_id', '=', 'c.id')->orderBy('num', 'desc')->whereIn('v.app_id', $appIds)->whereNotIn('v.status',$this->scopeStatus)->groupBy([
                'c.id',
                'c.name',
                'c.pid'
            ]);
            if ($this->timeVaild) {
                $start = Carbon::parse($this->startime)->startOfDay();
                $end   = Carbon::parse($this->endTime)->endOfDay();
                $builder = $builder->whereBetween('v.created_at',[$start,$end]);
            }
            $category = $builder->limit(5)->get();
            //赋值属性
            $this->cate = $category->mapWithKeys(function ($item) {
                return [$item->id => $item->name];
            })->toArray();

        }

        return $category;
    }


    /**
     * 获取等级数据
     * @return mixed
     */
    public function getLevel () {
        $levels = DB::table('level')->get();
        $this->level = $levels->mapWithKeys(function ($item) {
            return [$item->id => $item->name];
        })->toArray();
        return $levels;
    }

    /**
     * 初始化数据
     * @param $ogzPid
     * @return array $res
     */
    public function getComposition ($ogzPid) {
        $res = [];
        $accessIds = access()->hasOgzs();
        $ogz = $this->getOgz($ogzPid);

        if (!$ogz->isEmpty()) {
            foreach ($ogz as $k => $v) {
                if (in_array($v->id, $accessIds)) {
                    //ogz_name
                    $res[$k]['oid'] = $v->id;
                    $res[$k]['oname'] = $v->name;

                    if (isset($v->child)) {
                        //不包含父节点,子节点的所有
                        $ogzIds = array_merge(Ogz::getChildNoLayer($v->child), [$v->id]);
                        //apps
                        $res[$k]['apps'] = $this->getApp($ogzIds);
                    }
                    else {
                        $res[$k]['apps'] = $this->getApp([$v->id]);
                    }
                }

            }
        }

        return $res;
    }

    /**
     * 获取修复数,修复标准是漏洞状态是‘已关闭’
     * @param array $status 漏洞关闭状态
     * @param int $pid 具体的机构对应的app,不是权限下所有的app
     * @param array $appIds 应用id集合
     * @return int 返回修复数数量
     */
    public function getConfirmedNum ($pid, $status = [Vuls::SOLVED,Vuls::CLOSED],$appIds=[]) {
        $confirmedNum = collect();
        if (intval($status) && is_int($pid)) {

            if (is_int($pid) && $pid > 0) {

                $this->setApps($pid);
            } else {
                $this->appIds = $appIds;
            }

            $confirmedNum = DB::table('vuls')->whereIn('status' , $status)->whereIn('app_id', $this->appIds);
            //全局过滤条件
            $confirmedNum = $this->getScopeWhere($confirmedNum);
            if ($this->timeVaild) {
                $confirmedNum = $this->getTimeVaildWhere('created_at', $confirmedNum);
            }
        }
        return $confirmedNum->count();
    }
    /**
     * 获取不同级别的修复数量,修复标准是漏洞状态是‘已关闭’
     * @param array $status 漏洞关闭状态
     * @param int $level 级别
     * @return mixed 返回修复数
     */
    public function getConfirmedNumByLevel ($level,$status = [Vuls::SOLVED,Vuls::CLOSED]) {
        $confirmedNum = collect();
        if (intval($status) && is_int($level)) {

            $confirmedNum = DB::table('vuls')->where(['level_id' => $level])->whereIn('status',$status)->whereIn('app_id', $this->appIds);
            //全局过滤条件
            $confirmedNum = $this->getScopeWhere($confirmedNum);
            if ($this->timeVaild) {
                $confirmedNum = $this->getTimeVaildWhere('created_at', $confirmedNum);
            }
        }
        return $confirmedNum->count();
    }

    /**
     * 获取指定应用的总数量,不记录状态情况下的总数
     * @param int $pid
     * @param array $appIds
     * @return int 返回总数
     */
    public function getTotal ($pid,$appIds=[]) {

        if ($pid > 0) {

            $this->setApps($pid);
        } else {
            $this->appIds = $appIds;
        }
        $total = DB::table('vuls')->whereIn('app_id', $this->appIds);
        //全局过滤条件
        $total = $this->getScopeWhere($total);
        if ($this->timeVaild) {
            $total = $this->getTimeVaildWhere('created_at', $total);
        }
        return $total->count();
    }

    /**
     * 获取不同级别的发现数量
     * @param array $level
     * @param int $pid
     * @param array $appIds 应用id集合
     * @return int $findNum 返回数量
     */
    public function getFindNum ($level, $pid = 0,$appIds=[]) {
        if ($pid > 0) {

            $this->setApps($pid);
        }else {
            $this->appIds = $appIds;
        }
        $findNum = collect();
        if (is_array($level) ) {
            $findNum = DB::table('vuls')->whereIn('level_id', $level)->whereIn('app_id', $this->appIds);
            //全局过滤条件
            $findNum = $this->getScopeWhere($findNum);
            if ($this->timeVaild) {

                $findNum = $this->getTimeVaildWhere('created_at',$findNum);
            }
            //'已审核'状态为新增数量标准
            // $findNum->where(['status'=>Vuls::REVIEWED]);
            //无状态统计全量
        }
        return $findNum->count();
    }

    /**
     * 获取父级分类名称
     * @param int $pid
     * @return string $name
     */
    public function getPcateName ($pid) {
        $name = '';
        if (is_int($pid)) {
            $name = DB::table('category')->where(['id' => $pid])->value('name');
        }
        return $name;
    }

    /**
     * 机构子级所拥有的app,如果没有,取机构本身的app,特殊情况
     * @param int $pid
     * @return array $apps
     */
    public function getAppComplex ($pid) {
        $apps = [];
        //拥有的机构权限集合
        $accessIds = access()->hasOgzs();
        //生成相应的apps
        $this->getComposition($pid);

        if ($this->appIds) {
            $apps = $this->appIds;
        }
        else {
            $apps = App::getAppByOgzIds([$pid]);
            //叶子节点app
            $this->appIds = $apps;
        }

        return $apps;
    }

    /**
     * 设置apps
     * @param int $pid
     */
    private function setApps ($pid) {
        //因为是具体的机构对应的app,所以需要置空权限下所有的值
        $this->appIds = [];
        //生成指定机构的app
        $this->getAppComplex($pid);
        //app没在叶子节点下,加入当前机构拥有的app
        if (!$this->onlyLeaf) {
            if (isset($this->app[$pid])) {

                $this->appIds = array_merge($this->appIds,array_keys($this->app[$pid]));
            }
        }
    }

    /**
     * 根据时间获取不同的结果集合,创建时间字段created_at,修复时间字段updated_at
     * @param string $timeColumn
     * @param string $startime
     * @param string $endTime
     * @return int 数量
     */
    public function getVulByTime ($timeColumn,$startime,$endTime) {
        $ret = collect();
        if ($timeColumn && $startime && $endTime) {

            $ret = DB::table('vuls')->whereBetween($timeColumn, [
                $startime,
                $endTime
            ])->whereIn('app_id', $this->appIds);
            //全局过滤条件
            $ret = $this->getScopeWhere($ret);
        }
        return $ret->count();
    }

    /**
     * 根据时间获取不同的结果集合,创建时间字段created_at,修复时间字段updated_at
     * @param string $timeColumn
     * @param string $startime
     * @param string $endTime
     * @return mixed
     */
    public function getVulByTimeRetBuilder ($timeColumn,$startime,$endTime) {
        $ret = collect();
        if ($timeColumn && $startime && $endTime) {

            $ret = DB::table('vuls')->whereBetween($timeColumn, [
                $startime,
                $endTime
            ])->whereIn('app_id', $this->appIds);

        }
        return $ret;
    }

    /**
     * 设置时间过滤条件
     * @param string $field
     * @param mixed $queryBuilder
     * @return mixed
     */
    private function getTimeVaildWhere ($field,$queryBuilder) {
        //默认时间参数
        $start = $end = '';
        //自定义时间参数
        if ($this->startime) {
            //兼容时间戳和time类型
            $start = Carbon::parse($this->startime)->startOfDay();
            $end   = Carbon::parse($this->endTime)->endOfDay();
        }
        return $queryBuilder->whereBetween($field, [
            $start,
            $end
        ]);
    }

    /**
     * 获取对象应用id集合
     */
    public function getAppIds () {
        return $this->appIds;
    }

    /**
     * 获取全局额外条件
     * @param mixed $builder
     * @return mixed
     */
    public function getScopeWhere ($builder) {
        return $builder->whereNotIn('status',$this->scopeStatus);
    }

}